// 飞剑术
// 扇形攻击弹道，可穿透
library SimulateAttac2 initializer init requires SimulateAttack

    // TODO 怪物坐标0的问题

    globals
        // 飞剑等级（默认是1,0表示没有该技能，该技能是每个角色的基础技能）
        integer array ct_flyLevel
        // 飞剑释放CD（默认1秒）
        real array ct_flyCD
        // 飞剑数量（默认1）
        integer array ct_flyCount
        // 飞剑伤害增幅（默认0%）
        real array ct_flyDmZf
        // 飞剑命中后是否爆炸（默认false）
        boolean array ct_flyBoom
        // 飞剑命中单位后爆炸范围（默认150）
        real array ct_flyBoomRang
        // 飞剑命中单位爆炸范围增幅（默认0%）
        real array ct_flyBoomRangZf
        // 飞剑命中单位后爆炸伤害增幅（默认0%）
        real array ct_flyBoomDmZf
        // 飞剑贯穿个数（默认0，表示不贯穿，打到单位就消失）
        integer array ct_flyPzCount
        // 是否是火焰剑（默认false）
        boolean array ct_flyFire
        // 火焰剑灼烧伤害增幅（默认0%）
        real array ct_flyFireDmZf
        // 飞剑释放距离（默认2000）
        real array ct_flyDistance
        // 飞剑连射次数(默认0)
        integer array ct_flyAttCount


        // 飞剑术要灼烧的单位，飞剑术每秒灼烧一次，灼烧5秒
        // 父key 玩家id
        // 子key 1表示要玩家要灼烧的怪物
        hashtable hashG = InitHashtable()
    endglobals

    private function init takes nothing returns nothing
        local integer uid = 0
        loop
            exitwhen uid > PLAYER_NUMBER
            set ct_flyLevel[uid] = 1
            set ct_flyCD[uid] = 1.5
            set ct_flyCount[uid] = 1
            set ct_flyDmZf[uid] = 0.0
            set ct_flyBoom[uid] = false
            set ct_flyBoomRang[uid] = 180.0
            set ct_flyBoomRangZf[uid] = 0.0
            set ct_flyBoomDmZf[uid] = 0.0
            set ct_flyPzCount[uid] = 0
            set ct_flyFire[uid] = false
            set ct_flyFireDmZf[uid] = 0.0
            set ct_flyDistance[uid] = 2000.0
            set ct_flyAttCount[uid] = 0

            call SaveGroupHandle(hashG, uid, 1, CreateGroup())

            set uid = uid + 1
        endloop
    endfunction

    // 灼烧单位(玩家1)
    private function firingUnit1 takes nothing returns nothing 
        local integer uid = 0
        local unit u = udg_player_select_hero[uid]
        local unit bu = GetEnumUnit()
        local real damage = FF_GetAttack(u) * 0.1 * (ct_flyFireDmZf[uid] + 1.0)
        // 灼烧的次数
        local integer count

        if bu == null or FF_GetHp(bu) < 1.0 then
            call GroupRemoveUnit(LoadGroupHandle(hashG, uid, 1), bu)
        else
            set count = FF_TEMP_GetSK1(bu) + 1
            call FF_TEMP_SetSK1(bu, count)
            if count <= 5 then
                call UnitDamageTarget( u, GetEnumUnit(), damage, false, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_FIRE, WEAPON_TYPE_WHOKNOWS )
            else
                // 灼烧结束
                call FF_TEMP_SetSK1(bu, 0)
                call UnitRemoveAbility(bu, 'A004')
                call GroupRemoveUnit(LoadGroupHandle(hashG, uid, 1), bu)
            endif
        endif
        set u = null
        set bu = null
    endfunction
    // 灼烧单位(玩家2)
    private function firingUnit2 takes nothing returns nothing 
        local integer uid = 1
        local unit u = udg_player_select_hero[uid]
        local real damage = FF_GetAttack(u) * 0.1 * (ct_flyFireDmZf[uid] + 1.0)

        call UnitDamageTarget( u, GetEnumUnit(), damage, false, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_FIRE, WEAPON_TYPE_WHOKNOWS )
        set u = null
    endfunction
    // 灼烧单位(玩家3)
    private function firingUnit3 takes nothing returns nothing 
        local integer uid = 2
        local unit u = udg_player_select_hero[uid]
        local real damage = FF_GetAttack(u) * 0.1 * (ct_flyFireDmZf[uid] + 1.0)

        call UnitDamageTarget( u, GetEnumUnit(), damage, false, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_FIRE, WEAPON_TYPE_WHOKNOWS )
        set u = null
    endfunction
    // 灼烧单位(玩家4)
    private function firingUnit4 takes nothing returns nothing 
        local integer uid = 3
        local unit u = udg_player_select_hero[uid]
        local real damage = FF_GetAttack(u) * 0.1 * (ct_flyFireDmZf[uid] + 1.0)

        call UnitDamageTarget( u, GetEnumUnit(), damage, false, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_FIRE, WEAPON_TYPE_WHOKNOWS )
        set u = null
    endfunction

    // 每秒执行一次
    function fly_Every_Second takes nothing returns nothing
        local integer uid = 0
        if ct_flyFire[uid] then
            call ForGroup(LoadGroupHandle(hashG, 0, 1), function firingUnit1)
        endif
        // call BJDebugMsg("总池子怪物：" + I2S(CountUnitsInGroup(getMonsterGroup(0))))
        // call BJDebugMsg("剩余灼烧单位：" + I2S(CountUnitsInGroup(LoadGroupHandle(hashG, 0, 1))))
        set uid = 1
        if ct_flyFire[uid] then
            call ForGroup(LoadGroupHandle(hashG, 1, 1), function firingUnit2)
        endif
        set uid = 2
        if ct_flyFire[uid] then
            call ForGroup(LoadGroupHandle(hashG, 2, 1), function firingUnit3)
        endif
        set uid = 3
        if ct_flyFire[uid] then
            call ForGroup(LoadGroupHandle(hashG, 3, 1), function firingUnit4)
        endif
    endfunction

    // 飞剑命中爆炸
    // 飞剑命中怪物后对100码范围造成攻击*0.5的伤害
    private function flyBoom takes integer uid, real x, real y returns nothing
        local group g = CreateGroup()
        local boolexpr filter
        local unit ydl_unit
        local unit u = udg_player_select_hero[uid]
        local effect ect
        local real shv = FF_GetAttack(u) * 0.5 * (1.0 + ct_flyBoomDmZf[uid])
        // 范围增幅
        local real zf = ct_flyBoomRangZf[uid] + 1.0

        // 特效 
        if tx_show_status[uid] == 1 then
            set ect = AddSpecialEffect("tx_feijian_bz.mdx", x, y)
            call EXSetEffectSize(ect, 1.2 * zf)
            call DestroyEffect(ect)
        endif

        set filter = Condition(function heroSkillConditions)
        call GroupEnumUnitsInRange(g, x, y, ct_flyBoomRang[uid] * zf, filter)
        loop
            set ydl_unit = FirstOfGroup(g)
            exitwhen (ydl_unit == null)
            call GroupRemoveUnit(g, ydl_unit)
            call UnitDamageTarget( u, ydl_unit, shv, false, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_ENHANCED, WEAPON_TYPE_WHOKNOWS )
        endloop

        call DestroyBoolExpr(filter)
        call DestroyGroup(g)

        set ect = null
        set u = null
        set g = null
        set filter = null
        set ydl_unit = null
    endfunction

    // 判断单位是否被指定弹道攻击过，攻击过返回true，否则返回false
    private function isAttack takes unit u, integer id returns boolean
        local integer total = LoadInteger(skht, id, StringHash("穿透个数"))
        local integer i = 1
        loop
            exitwhen i > total
            if LoadInteger(skht, id, i) == GetHandleId(u) then
                return true
            endif
            set i = i + 1
        endloop

        set u = null
        return false
    endfunction

    // 弹道伤害  id为弹道id
    function fan_shaped_damage takes integer uid, integer tid, integer tidi, integer id, real x, real y, real radius, real damage returns nothing

        local group g = CreateGroup()
        local boolexpr filter
        local unit ydl_unit
        local unit u = udg_player_select_hero[uid]
        local integer total = LoadInteger(skht, id, StringHash("穿透个数"))
        local integer count = LoadInteger(skht, id, StringHash("当前穿透个数"))
        
        local integer r

        set filter = Condition(function heroSkillConditions)
        call GroupEnumUnitsInRange(g, x, y, radius, filter)

        loop
            set ydl_unit = FirstOfGroup(g)
            exitwhen (ydl_unit == null)
            // 判断该单位是否已经被该弹道攻击过
            call GroupRemoveUnit(g, ydl_unit)
            if not isAttack(ydl_unit, id) and count <= total then
                // 伤害
                set count = count + 1
                call SaveInteger(skht, id, StringHash("当前穿透个数"), count)
                call SaveInteger(skht, id, count, GetHandleId(ydl_unit))

                // 范围爆炸
                if ct_flyBoom[uid] then
                    // 防止被其他弹道打死获取的位置是0
                    if GetUnitX(ydl_unit) == 0.0 and GetUnitY(ydl_unit) == 0.0 then
                        call flyBoom(uid, x, y)
                    else
                        call flyBoom(uid, GetUnitX(ydl_unit), GetUnitY(ydl_unit))
                    endif
                endif

                call UnitDamageTarget( u, ydl_unit, damage, true, true, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS )

                // 如果是火焰剑，进行每秒燃烧伤害
                if ct_flyFire[uid] and GetUnitAbilityLevel(ydl_unit, 'A004') < 1 and FF_GetHp(ydl_unit) >= 1 then
                    call UnitAddAbility(ydl_unit, 'A004')
                    call GroupAddUnit(LoadGroupHandle(hashG, uid, 1), ydl_unit)
                endif

                // call DisplayTextToPlayer(Player(0), 0, 0, "count = " + I2S(count) + "total = " + I2S(total) + "handle = " + )

                // 如果穿透数量达到了，销毁弹道
                if count > total then
                    call DestroyEffect(LoadEffectHandle(skht, tid, tidi))
                    call RemoveSavedHandle(skht, tid, tidi)
                endif
            endif
        endloop

        call DestroyBoolExpr(filter)
        call DestroyGroup(g)

        set u = null
        set g = null
        set filter = null
        set ydl_unit = null
    endfunction

    function timer_fan_shaped takes nothing returns nothing
        local timer t = GetExpiredTimer()
        local integer tid = GetHandleId(t)
        local integer uid = LoadInteger(skht, tid, 1)
        local integer num = LoadInteger(skht, tid, 2)
        local real firstLineAngle = LoadReal(skht, tid, 3)
        local real inAngle = LoadReal(skht, tid, 5)
        local integer loopi
        local integer ecthid
        local integer count
        local integer total

        // 每次要移动的距离
        local real jl = LoadReal(skht, tid, 4) + 35
    
        // 计算出马特效的位置
        local real x
        local real y
        // 总距离
        local real totalJl = LoadReal(skht, tid, 12)

        set loopi = 0
        loop
            exitwhen loopi >= num
            
            if LoadEffectHandle(skht, tid, 20 + loopi) != null then
                set x = CosBJ(firstLineAngle) * (jl) + LoadReal(skht, tid, 6)
                set y = SinBJ(firstLineAngle) * (jl) + LoadReal(skht, tid, 7)
                // 设置弹道位置
                call EXSetEffectXY( LoadEffectHandle(skht, tid, 20 + loopi), x, y )

                // 对单位造成伤害，并且删除弹道
                set ecthid = GetHandleId(LoadEffectHandle(skht, tid, 20 + loopi))
                set total = LoadInteger(skht, ecthid, StringHash("穿透个数"))
                set count = LoadInteger(skht, ecthid, StringHash("当前穿透个数"))

                // TODO 数量，地形
                if count > total then
                    call DestroyEffect(LoadEffectHandle(skht, tid, 20 + loopi))
                    call RemoveSavedHandle(skht, tid, 20 + loopi)
                else
                    call fan_shaped_damage(uid, tid, 20 + loopi, ecthid, x, y, 60.0, FF_GetAttack(udg_player_select_hero[uid]) * (1.0 + ct_flyDmZf[uid]))
                endif
            endif

            set firstLineAngle = firstLineAngle + inAngle
            set loopi = loopi + 1
        endloop

        // 退出计时
        call SaveReal(skht, tid, 4, jl)
        if(jl >= totalJl) then

            set loopi = 0
            loop
                exitwhen loopi >= num
                set ecthid = GetHandleId(LoadEffectHandle(skht, tid, 20 + loopi))
                call DestroyEffect(LoadEffectHandle(skht, tid, 20 + loopi))
                call FlushChildHashtable(skht, ecthid)
                set loopi = loopi + 1
            endloop
            call FlushChildHashtable(skht, tid)
            call PauseTimer(t)
            call DestroyTimer(t)
        endif
    
        set t = null
    endfunction

    // 释放2 速度稍微快点，目前只有攻家BOSS在使用
    function releaseFanAttack2 takes integer uid, real order_x, real order_y, real distance, real angle, real inAngle, integer num, real height, string mdl returns nothing

        
        // uid - 释放玩家
        // order_x - 中心点x
        // order_y - 中心点y
        // distance - 每根线的长度距离
        // angle - 角度：箭矢射向的方向
        // inAngle - 夹角角度
        // num - 扇形数量
        // mdl - 特效模型
        // height - 特效高度
        
        // 1 根线的时候 角度是0
        // 2 根线 有 一个夹角  3 根线 有 两个夹角 也就是 num-1就是夹角数量
        // 问题在于求出第一个线的角度？
        // 1 根线的时候 角度是0                         0个夹角0
        // 2 根线的时候 角度是 angle/2                  半个夹角0.5
        // 3 根线的时候 角度是 angle                    一个夹角1
        // 4 根线的时候 角度是 angle/2 + angle          一个半夹角1.5
        // 5 根线的时候 角度是 angle*2                  两个夹角2
        // 6 根线的时候 角度是 angle/2 + angle*2        两个半夹角2.5
        // 7 根线的时候 角度是 angle + angle*2          三个夹角3

        // 第一条线的角度
        local real firstLineAngle
        local integer loopi
        local real jd
        local timer t
        local integer tid
        local effect ect
        local integer ecthid

        // 根据线的条数求出第一个线的角度
        // (num - 1) * 0.5
        set firstLineAngle = angle - I2R(num - 1) * 0.5 * inAngle

        set t = CreateTimer()
        set tid = GetHandleId(t)
        call SaveInteger(skht, tid, 1, uid)
        call SaveInteger(skht, tid, 2, num)
        call SaveReal(skht, tid, 3, firstLineAngle)
        call SaveReal(skht, tid, 5, inAngle)
        call SaveReal(skht, tid, 6, order_x)
        call SaveReal(skht, tid, 7, order_y)
        // 距离
        call SaveReal(skht, tid, 12, distance)
        
        set loopi = 0
        loop
            exitwhen loopi >= num
            
            set ect = AddSpecialEffect(mdl, order_x, order_y)
            call EXSetEffectZ( ect, getZByXY(order_x, order_y) + height )
            call EXEffectMatRotateZ(ect, firstLineAngle)

            call SaveEffectHandle(skht, tid, 20 + loopi, ect)    

            // 弹道要穿透的单位个数，最多20个  为0表示不穿透
            // 子key 1 - 穿透数量用来保存穿透的单位handid,避免重复穿透
            // 当前穿透的个数 达到 穿透个数结束弹道
            set ecthid = GetHandleId(ect)
            call SaveInteger(skht, ecthid, StringHash("穿透个数"), ct_flyPzCount[uid])
            call SaveInteger(skht, ecthid, StringHash("当前穿透个数"), 0)


            set firstLineAngle = firstLineAngle + inAngle
            set loopi = loopi + 1
        endloop

        call TimerStart(t, 0.01, true, function timer_fan_shaped)

        set mdl = null
        set t = null
        set ect = null
    endfunction

    function timer_relFanAtk takes nothing returns nothing
        local timer t = GetExpiredTimer()
        local integer tid = GetHandleId(t)

        local integer uid = LoadInteger(skht, tid, 1)
        local real order_x = LoadReal(skht, tid, 2)
        local real order_y = LoadReal(skht, tid, 3)
        local real dis = LoadReal(skht, tid, 4)
        local real inAngle = LoadReal(skht, tid, 6)
        local integer num = LoadInteger(skht, tid, 7)
        local real height = LoadReal(skht, tid, 8)
        local string mdl = LoadStr(skht, tid, 9)
        local integer count = LoadInteger(skht, tid, 10) + 1

        local unit bu = getNearestUnit(uid)
        local real angle = getAngleBetweenXY(GetUnitX(udg_player_select_hero[uid]), GetUnitY(udg_player_select_hero[uid]), GetUnitX(bu), GetUnitY(bu))

        if num > 1 then
            set inAngle = 5.0
            // 多个飞剑的时候飞剑稍微偏差一点角度
            set angle = angle + inAngle / 2.0
        endif

        call releaseFanAttack2(uid, order_x, order_y, dis, angle, inAngle, num, height, mdl)

        call SaveInteger(skht, tid, 10, count)
        if count >= ct_flyAttCount[uid] then
            call FlushChildHashtable(skht, tid)
            call DestroyTimer(t)
        endif

        set bu = null
        set mdl = null
        set t = null
    endfunction

    // 释放飞剑
    function releaseFanAttack takes integer uid, real order_x, real order_y returns nothing
        
        local unit bu = getNearestUnit(uid)
        local real angle = getAngleBetweenXY(GetUnitX(udg_player_select_hero[uid]), GetUnitY(udg_player_select_hero[uid]), GetUnitX(bu), GetUnitY(bu))
        local integer num = ct_flyCount[uid]
        local real inAngle = 0.0
        local real height = 50.0
        // local string mdl = "tsw_feijian.mdx"
        // local string mdl = "tsw_feijian2.mdx"
        local string mdl = "tsw_feijian3.mdx"
        local timer t
        local integer tid

        if num > 1 then
            set inAngle = 5.0
            // 多个飞剑的时候飞剑稍微偏差一点角度
            set angle = angle + inAngle / 2.0
        endif
        // 是否火焰剑
        if ct_flyFire[uid] then
            set mdl = "tsw_feijian_fire.mdx"
            // set mdl = "tsw_feijian_fire2.mdx"
            // set mdl = "tsw_feijian_fire3.mdx"
        endif

        call releaseFanAttack2(uid, order_x, order_y, ct_flyDistance[uid], angle, inAngle, num, height, mdl)

        if ct_flyAttCount[uid] > 0 then
            set t = CreateTimer()
            set tid = GetHandleId(t)
            call SaveInteger(skht, tid, 1, uid)
            call SaveReal(skht, tid, 2, order_x)
            call SaveReal(skht, tid, 3, order_y)
            call SaveReal(skht, tid, 4, ct_flyDistance[uid])
            // call SaveReal(skht, tid, 5, angle)
            call SaveReal(skht, tid, 6, inAngle)
            call SaveInteger(skht, tid, 7, num)
            call SaveReal(skht, tid, 8, height)
            call SaveStr(skht, tid, 9, mdl)

            call TimerStart(t, 0.5, true, function timer_relFanAtk)
        endif

        set bu = null
        set mdl = null
        set t = null
    endfunction
endlibrary



